<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="F2Dih" id="F2Dih"><span data-lake-id="ub42262dc" id="ub42262dc">典型回答</span></h1>
  <p data-lake-id="ub4bfea71" id="ub4bfea71"><br></p>
  <p data-lake-id="ufbbd0d89" id="ufbbd0d89"><span data-lake-id="ubd72c45e" id="ubd72c45e">SQL调优是面试中经常爱问的问题，这个问题可以考察一个候选人对于SQL的整体性能优化的理解和掌握程度，一般来说，SQL调优需要从以下几个方面和步骤入手。</span></p>
  <p data-lake-id="u60525159" id="u60525159"><span data-lake-id="uad5e1aa5" id="uad5e1aa5">​</span><br></p>
  <p data-lake-id="u5e076367" id="u5e076367"><span data-lake-id="u75945f51" id="u75945f51">首先，需要先发现问题，尤其是在面试中，</span><strong><span data-lake-id="uc713fe24" id="uc713fe24">最好是结合业务说明</span></strong><span data-lake-id="ua4da7bef" id="ua4da7bef">，比如是某一次线下报警出现了慢SQL，或者是接口RT比较长，做了性能分析发现瓶颈是在SQL查询上面都可以。但是不管怎么样，一定要有背景。</span></p>
  <p data-lake-id="u55d1f04e" id="u55d1f04e"><span data-lake-id="u1766d5c5" id="u1766d5c5">​</span><br></p>
  <p data-lake-id="u8a22e62d" id="u8a22e62d"><span data-lake-id="uad854075" id="uad854075">有了问题之后，那就是问题的分析了。</span></p>
  <p data-lake-id="u2637eacc" id="u2637eacc"><span data-lake-id="u47a454a8" id="u47a454a8">​</span><br></p>
  <p data-lake-id="u8f198497" id="u8f198497"><span data-lake-id="ua6a5cad0" id="ua6a5cad0">首先需要定位到具体的SQL语句，这个可以通过各类监控平台或者工具来实现，通过定位到SQL语句之后，我们就知道具体是哪张表、哪个SQL慢了。</span></p>
  <p data-lake-id="u83924cd4" id="u83924cd4"><span data-lake-id="u5d161a01" id="u5d161a01">​</span><br></p>
  <p data-lake-id="ub904d378" id="ub904d378"><span data-lake-id="ucb510030" id="ucb510030">那接下来就是进行分析了，一般一个SQL慢，可能有以下几种原因：</span></p>
  <p data-lake-id="u15217d21" id="u15217d21"><span data-lake-id="u1de0ebe6" id="u1de0ebe6">​</span><br></p>
  <p data-lake-id="u24032c3a" id="u24032c3a"><span data-lake-id="ua6ad31ba" id="ua6ad31ba">1、索引失效</span></p>
  <p data-lake-id="u369bc7e0" id="u369bc7e0"><span data-lake-id="u5787f69e" id="u5787f69e">2、多表join</span></p>
  <p data-lake-id="u88c3170d" id="u88c3170d"><span data-lake-id="u5273c288" id="u5273c288">3、查询字段太多</span></p>
  <p data-lake-id="uac86e865" id="uac86e865"><span data-lake-id="ue35757f8" id="ue35757f8">4、表中数据量太大</span></p>
  <p data-lake-id="u5dd85927" id="u5dd85927"><span data-lake-id="ua7f1d957" id="ua7f1d957">5、索引区分度不高</span></p>
  <p data-lake-id="u956d2855" id="u956d2855"><span data-lake-id="ub5448ea3" id="ub5448ea3">6、数据库连接数不够</span></p>
  <p data-lake-id="u030e2bea" id="u030e2bea"><span data-lake-id="ua6a51ce5" id="ua6a51ce5">7、数据库的表结构不合理</span></p>
  <p data-lake-id="ue4dde7cb" id="ue4dde7cb"><span data-lake-id="u2261cbe9" id="u2261cbe9">8、数据库IO或者CPU比较高</span></p>
  <p data-lake-id="ub3271d72" id="ub3271d72"><span data-lake-id="u72d2b3cb" id="u72d2b3cb">9、数据库参数不合理</span></p>
  <p data-lake-id="udda47880" id="udda47880"><span data-lake-id="udeef13ff" id="udeef13ff">10、事务比较长</span></p>
  <p data-lake-id="u3771215a" id="u3771215a"><span data-lake-id="uae377f32" id="uae377f32">11、锁竞争导致的等待</span></p>
  <p data-lake-id="u43e75b63" id="u43e75b63"><span data-lake-id="u4053204e" id="u4053204e">​</span><br></p>
  <p data-lake-id="u2af97a65" id="u2af97a65"><span data-lake-id="u64f047f9" id="u64f047f9">所以，一次完整的SQL调优，一般需要考虑以上几个因素，一般会涉及到其中的一个或者多个问题。那么就逐个优化。</span></p>
  <p data-lake-id="u4e3f07e4" id="u4e3f07e4"><span data-lake-id="u30f3497f" id="u30f3497f">​</span><br></p>
  <p data-lake-id="u1ae05b0c" id="u1ae05b0c"><span data-lake-id="uc18f42d6" id="uc18f42d6">​</span><br></p>
  <p data-lake-id="u845a33a3" id="u845a33a3"><span data-lake-id="u466350d0" id="u466350d0">首先，</span><strong><span data-lake-id="ud6ff59bc" id="ud6ff59bc">索引失效的问题</span></strong><span data-lake-id="ufe5708c0" id="ufe5708c0">一般是先通过执行计划分析是否走了索引，以及所走的索引是否符合预期，如果因为索引设计的不合理、或者索引失效导致的，那么就可以修改索引，或者修改SQL语句。或者强制执行使用某个索引。具体可以参考：</span></p>
  <p data-lake-id="ue470ec8b" id="ue470ec8b"><span data-lake-id="u09fe9cc0" id="u09fe9cc0">​</span><br></p>
  <p data-lake-id="u0c77f362" id="u0c77f362"><br></p>
  <p data-lake-id="uaad81504" id="uaad81504"><span data-lake-id="u30cfca9b" id="u30cfca9b">其次，</span><strong><span data-lake-id="u3f192daa" id="u3f192daa">多表join</span></strong><span data-lake-id="u832d37d0" id="u832d37d0">也是SQL执行的比较慢的一个常见原因，关于这个问题，我们在以下文章中有详细的阐述背景和解决方案：</span></p>
  <p data-lake-id="u02fa5de1" id="u02fa5de1"><span data-lake-id="u1cb1bd1d" id="u1cb1bd1d">​</span><br></p>
  <p data-lake-id="u5f25f128" id="u5f25f128"><br></p>
  <p data-lake-id="u648928b7" id="u648928b7"><span data-lake-id="u6ef5075e" id="u6ef5075e">接下来，如果是索引区分度不高的话，这个其实也和索引不合理有关，但是其实到底快不快，用不用索引，并不是因为区分度高不高导致，其实还是索引扫描的行数的成本导致。所以，有的时候不能认为区分度不高就一定会效率低，或者一定就不适合创建索引。</span></p>
  <p data-lake-id="u61b19f88" id="u61b19f88"><span data-lake-id="u530c1913" id="u530c1913">​</span><br></p>
  <p data-lake-id="u0d338c06" id="u0d338c06"><br></p>
  <p data-lake-id="ub85baefb" id="ub85baefb"><strong><span data-lake-id="ub549a0d5" id="ub549a0d5">查询字段太多</span></strong><span data-lake-id="uec79d012" id="uec79d012">，这个有的时候是因为我们错误的用到了select * 导致的，一般来说，查询字段小于100个，都不是特别大的问题，除非真的是字段数特别多，这时候可以采用两种办法解决。第一个就是不要查询那些你不关心的字段，只查询少部分字段。第二个就是做分表，垂直分表，把数据拆分到多张表中。但是这么做可能也会带来需要多表join的问题，所以拆分的时候也需要考虑冗余。</span></p>
  <p data-lake-id="ufd4a5bcf" id="ufd4a5bcf"><span data-lake-id="u7b1658c2" id="u7b1658c2">​</span><br></p>
  <p data-lake-id="u80ca0685" id="u80ca0685"><strong><span data-lake-id="uf3f3ebf8" id="uf3f3ebf8">表中数据量太大</span></strong><span data-lake-id="u078dd8fa" id="u078dd8fa">，一般来说，单表超过1000万，会导致查询效率变低，即使使用索引可能也会比较慢，所以如果表中数据量太大的话，这时候可能通过建索引并不一定能完全解决了。那么具体的解决方案有几种：</span></p>
  <p data-lake-id="u0ef85e30" id="u0ef85e30"><span data-lake-id="u7e104147" id="u7e104147">​</span><br></p>
  <p data-lake-id="u58f60068" id="u58f60068"><span data-lake-id="ub876123c" id="ub876123c">1、数据归档，把历史数据移出去，比如只保留最近半年的数据，半年前的数据做归档。</span></p>
  <p data-lake-id="ufccc9fae" id="ufccc9fae"><span data-lake-id="u9cbadf76" id="u9cbadf76">2、分库分表、分区。把数据拆分开，分散到多个地方去，这里不详细介绍了，我们的文档中有分库分表和分区的详细介绍，不展开了。</span></p>
  <p data-lake-id="u8b3ee4f5" id="u8b3ee4f5"><span data-lake-id="u89f9438a" id="u89f9438a">3、使用第三方的数据库，比如把数据同步到支持大数量查询的分布式数据库中，如oceanbase、tidb，或者搜索引擎中，如ES等。</span></p>
  <p data-lake-id="u4ce3ba68" id="u4ce3ba68"><span data-lake-id="ua35e41b7" id="ua35e41b7">​</span><br></p>
  <p data-lake-id="u8b55037c" id="u8b55037c"><strong><span data-lake-id="ue45bccff" id="ue45bccff">数据库连接数不够</span></strong><span data-lake-id="uc8b9f0de" id="uc8b9f0de">，这个也需要具体分析，到底是什么原因，可能的原因有几个，第一个就是业务量太大了，单库确实扛不住了，那就选择分库吧。</span></p>
  <p data-lake-id="u7c341e05" id="u7c341e05"><span data-lake-id="u79dc509e" id="u79dc509e">​</span><br></p>
  <p data-lake-id="u952cbdb0" id="u952cbdb0"><span data-lake-id="u96a6b262" id="u96a6b262">第二个可能就是存在一些慢SQL、或者长事务导致的，慢SQL占用数据库链接，数据库连接数不够，其他的查询就会阻塞，就更慢。</span></p>
  <p data-lake-id="u714da61a" id="u714da61a"><span data-lake-id="u2ebeaddc" id="u2ebeaddc">​</span><br></p>
  <p data-lake-id="u9894b6f0" id="u9894b6f0"><span data-lake-id="ubba11af0" id="ubba11af0">​</span><br></p>
  <p data-lake-id="uec744ab0" id="uec744ab0"><strong><span data-lake-id="u37500b08" id="u37500b08">数据库的表结构不合理</span></strong><span data-lake-id="ue3b3abf7" id="ue3b3abf7">，这个也是一个关键原因，有的时候比如某个字段中存了很长的内容，或者没有做合理的冗余需要多表关联查询等等。解决思路就是重构，或者分表。</span></p>
  <p data-lake-id="u60665086" id="u60665086"><span data-lake-id="u115feaa4" id="u115feaa4">​</span><br></p>
  <p data-lake-id="ud723d743" id="ud723d743"><strong><span data-lake-id="u9d528cff" id="u9d528cff">数据库IO或者CPU比较高</span></strong><span data-lake-id="ue1e94c7a" id="ue1e94c7a">，这种问题也常见的，当数据库整体IO或者CPU飙高的时候，查询速度就有可能下降，所以需要分析背后的原因及解决思路，可以参考：</span></p>
  <p data-lake-id="u88a6eba5" id="u88a6eba5"><span data-lake-id="u8888ffbb" id="u8888ffbb">​</span><br></p>
  <p data-lake-id="u7770142b" id="u7770142b"><span data-lake-id="ud5403470" id="ud5403470">​</span><br></p>
  <p data-lake-id="uc462ef4a" id="uc462ef4a"><strong><span data-lake-id="u1d31c6a1" id="u1d31c6a1">存在长事务</span></strong><span data-lake-id="u714d1c59" id="u714d1c59">，这个和慢SQL同理，都是占用了数据库链接，导致其他请求要等待。</span></p>
  <p data-lake-id="uc452e02f" id="uc452e02f"><span data-lake-id="u24fb4290" id="u24fb4290">​</span><br></p>
  <p data-lake-id="ubaa7b8fd" id="ubaa7b8fd"><strong><span data-lake-id="ub1118520" id="ub1118520">锁竞争导致的等待，</span></strong><span data-lake-id="ue48190de" id="ue48190de">当有大并发争抢共享资源的时候，就会导致锁等待，这个过程就会拉长耗时，导致SQL变慢。这个也可以参考上面的CPU被打满的问题。</span></p>
  <p data-lake-id="u86686b30" id="u86686b30"><br></p>
  <p data-lake-id="u66c6f766" id="u66c6f766"><strong><span data-lake-id="u78692dd5" id="u78692dd5">数据库参数不合理，</span></strong><span data-lake-id="u67e242cd" id="u67e242cd">这个也是经常会遇到的，针对我们具体的业务场景，做一些适当的参数调整，有时候也能大大的提升SQL的效率。比如调整内存大小、缓存大小、线程池大小等。</span></p>
  <p data-lake-id="u6d01566f" id="u6d01566f"><span data-lake-id="ua4fed0e6" id="ua4fed0e6">​</span><br></p>
  <p data-lake-id="u5978cd89" id="u5978cd89"><span data-lake-id="u011d6ab6" id="u011d6ab6">​</span><br></p>
  <h1 data-lake-id="hfbY2" id="hfbY2"><span data-lake-id="uf9cd4be5" id="uf9cd4be5">扩展知识</span></h1>
  <p data-lake-id="ucfc0f1d3" id="ucfc0f1d3"><br></p>
  <h2 data-lake-id="VzMXA" id="VzMXA"><span data-lake-id="ub3755347" id="ub3755347">参数优化</span></h2>
  <p data-lake-id="ueeb7d24e" id="ueeb7d24e"><br></p>
  <p data-lake-id="u52cc6050" id="u52cc6050"><span data-lake-id="u833a7962" id="u833a7962">假设我们有一个名为 mydb 的数据库，其中包含一个名为 mytable 的 InnoDB 表。该表有一个自增主键 id，一个整数类型字段 age 和一个字符串类型字段 name，我们希望对该表进行优化。</span></p>
  <p data-lake-id="u9028cd3a" id="u9028cd3a"><span data-lake-id="u58326611" id="u58326611">​</span><br></p>
  <p data-lake-id="u7a861542" id="u7a861542"><span data-lake-id="u3e37cbe0" id="u3e37cbe0">首先，我们可以使用 </span><span data-lake-id="ue20615b4" id="ue20615b4">SHOW VARIABLES LIKE 'innodb%';</span><span data-lake-id="ub52672dd" id="ub52672dd"> 命令查看当前的 InnoDB 参数设置。这些参数包括缓冲池大小、刷新间隔、日志大小等等。</span></p>
  <p data-lake-id="uf636b122" id="uf636b122"><span data-lake-id="uc1667dcb" id="uc1667dcb">​</span><br></p>
  <p data-lake-id="uffac7b3f" id="uffac7b3f"><span data-lake-id="u78e5b9ae" id="u78e5b9ae">接下来，我们可以尝试调整以下几个参数来优化数据库性能：</span></p>
  <p data-lake-id="u736ea1ea" id="u736ea1ea"><span data-lake-id="u4e5239cd" id="u4e5239cd">​</span><br></p>
  <p data-lake-id="u251b4333" id="u251b4333"><strong><span data-lake-id="u03593ec2" id="u03593ec2">innodb_buffer_pool_size</span></strong><span data-lake-id="u6497dbfc" id="u6497dbfc">: 缓冲池大小是 InnoDB 存储引擎的核心参数之一，它控制着 InnoDB 存储引擎使用的内存大小。通常，我们可以将该参数设置为系统可用内存的 70%-80%。例如，如果系统有 8GB 内存可用，我们可以将 innodb_buffer_pool_size 设置为 6GB。在 MySQL 中，可以使用以下命令进行设置：</span></p>
  <p data-lake-id="udf5ae9ea" id="udf5ae9ea"><span data-lake-id="ue24eb0b0" id="ue24eb0b0">​</span><br></p>
  <pre lang="java"><code>
SET GLOBAL innodb_buffer_pool_size=6G;
</code></pre>
  <p data-lake-id="u16dca66d" id="u16dca66d"><span data-lake-id="ud616f1ab" id="ud616f1ab">​</span><br></p>
  <p data-lake-id="uc73eddae" id="uc73eddae"><span data-lake-id="u9c2af0ef" id="u9c2af0ef">​</span><br></p>
  <p data-lake-id="u0cdea23b" id="u0cdea23b"><strong><span data-lake-id="u6d779c6c" id="u6d779c6c">innodb_read_io_threads</span></strong><span data-lake-id="udf1bedd8" id="udf1bedd8"> 和 </span><strong><span data-lake-id="u9a7cd2db" id="u9a7cd2db">innodb_write_io_threads</span></strong><span data-lake-id="ucaf6504d" id="ucaf6504d">: 这两个参数控制 InnoDB 存储引擎使用的 I/O 线程数量。</span><strong><span data-lake-id="ua62bd3ba" id="ua62bd3ba">通常情况下，我们可以将这两个参数设置为 CPU 核心数的一半。</span></strong><span data-lake-id="u0b43056b" id="u0b43056b">在 MySQL 中，可以使用以下命令进行设置：</span></p>
  <p data-lake-id="uba85f76a" id="uba85f76a"><span data-lake-id="u88f4d604" id="u88f4d604">​</span><br></p>
  <pre lang="java"><code>
SET GLOBAL innodb_read_io_threads=4;
SET GLOBAL innodb_write_io_threads=4;

</code></pre>
  <p data-lake-id="u77ae78f0" id="u77ae78f0"><br></p>
  <p data-lake-id="ucfc3cda6" id="ucfc3cda6"><br></p>
  <p data-lake-id="u18fe018a" id="u18fe018a"><strong><span data-lake-id="ua58ec580" id="ua58ec580">innodb_log_file_size</span></strong><span data-lake-id="uaf1cbdeb" id="uaf1cbdeb">: 这个参数控制事务日志文件的大小。这个值的默认为5M，是远远不够的。在 MySQL 中，可以使用以下命令进行设置：</span></p>
  <p data-lake-id="u5aa6f1cf" id="u5aa6f1cf"><span data-lake-id="u43589b89" id="u43589b89">​</span><br></p>
  <pre lang="java"><code>
SET GLOBAL innodb_log_file_size=1G;
</code></pre>
  <p data-lake-id="uea858226" id="uea858226"><span data-lake-id="ub9425939" id="ub9425939">​</span><br></p>
  <p data-lake-id="u89f6e6ea" id="u89f6e6ea"><span data-lake-id="u196ea6cf" id="u196ea6cf">一般在设置这个字段的时候，都是需要先进行数据采样，看一下一般在业务高峰期，2小时左右可以写入多少日志，然后把这个量作为你的日志文件的大小即可。如果简单点的话，一般设置为1G左右，或者系统内存的1/4即可。</span></p>
  <p data-lake-id="u55e52af2" id="u55e52af2"><span data-lake-id="ub479b27c" id="ub479b27c">​</span><br></p>
  <p data-lake-id="ud864308b" id="ud864308b"><span data-lake-id="u5f322227" id="u5f322227">​</span><br></p>
 </body>
</html>